
/*-------------------------------------------------------------------------
 *
 * regexp.c
 *	  Postgres 的正则表达式包接口。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/regexp.c
 *
 *		Alistair Crooks 添加了正则表达式缓存的代码
 *		agc - 缓存使用的正则表达式 - 有很大机会
 *		我们将得到一个命中，所以这为每次
 *		尝试匹配节省了编译步骤。我实际上还没有测量速度提升，
 *		但在观察回归测试输出时，它“看起来”视觉上快多了。
 *
 *		agc - 将 Keith Bostic 的伯克利正则表达式代码整合到
 *		所有平台的树中。为了区别这段正则表达式代码与任何
 *		在平台上存在的代码，我在
 *		regcomp、regerror、regexec 和 regfree 函数前添加了字符串 "pg_"。
 *		修复了一个我最初的拼写错误的bug，其中在编译
 *		正则表达式时使用了 "i" 而不是 "oldest" - 大多数是良性的
 *		结果，但有时会引起问题...
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "regex/regex.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/varlena.h"

#define PG_GETARG_TEXT_PP_IF_EXISTS(_n) \
	(PG_NARGS() > (_n) ? PG_GETARG_TEXT_PP(_n) : NULL)


/* 正则表达式函数的所有相关选项 */
typedef struct pg_re_flags
{
	int			cflags;			/* Spencer 的正则表达式代码的编译标志 */
	bool		glob;			/* 在全局范围内执行（针对每个出现） */
} pg_re_flags;

/* regexp_match 和 regexp_split 函数的交叉调用状态 */
typedef struct regexp_matches_ctx
{
	text	   *orig_str;		/* 原始 TEXT 形式的数据字符串 */
	int			nmatches;		/* 模式匹配的地方数量 */
	int			npatterns;		/* 捕获子模式的数量 */
	/* 我们为每个匹配存储开始字符索引和结束 + 1 字符索引 */
	/* 因此，match_locs 中的条目数量是 nmatches * npatterns * 2 */
	int		   *match_locs;		/* 0 基字符索引 */
	int			next_match;		/* 待处理的下一个匹配项的 0 基索引 */
	/* build_regexp_match_result() 的工作空间 */
	Datum	   *elems;			/* 具有 npatterns 元素 */
	bool	   *nulls;			/* 具有 npatterns 元素 */
	pg_wchar   *wide_str;		/* 原始字符串的宽字符版本 */
	char	   *conv_buf;		/* 转换缓冲区，如果需要 */
	int			conv_bufsiz;	/* 其大小 */
} regexp_matches_ctx;

/* 
 * 我们使用“自组织列表”结构缓存预编译的正则表达式，
 * 在这个结构中，最近使用的项倾向于靠近前面。
 * 每当我们使用一个条目时，它就会被移到列表的前面。
 * 随着时间的推移，项目的平均位置与其使用频率相对应。
 *
 * 当我们首次创建一个条目时，它会被插入到数组的前面，
 * 如果需要，将数组末尾的条目丢弃以腾出空间。
 * （这可能看起来对新条目的权重过重，
 * 但是如果我们在后面插入新条目，我们将无法调整到
 * 在查询混合中突然出现的情况，其中我们呈现了 MAX_CACHED_RES
 * 从未见过的循环使用项目。我们应该能够处理
 * 这种情况，因此我们必须插入到前面。）
 *
 * Knuth提到了一种变体策略，其中使用的项目仅向上移动
 * 列表中的一个位置。虽然他说这在平均上使用更少的比较，
 * 但它似乎并不适应你既有一些可重用模式又有稳定流的非可重用模式的情况。
 * 一个至少不如非可重用模式看到的频繁使用的可重用模式
 * 将“无法跟上”，并将从缓存末尾掉落。使用前移， 
 * 只要可重用模式在每 MAX_CACHED_RES 次使用中至少被使用一次，
 * 就保证会留在缓存中。
 */

/* 这是缓存的最大正则表达式数量 */
#ifndef MAX_CACHED_RES
#define MAX_CACHED_RES	32
#endif

/* 该结构描述一个缓存的正则表达式 */
typedef struct cached_re_str
{
	char	   *cre_pat;		/* 原始 RE（未以 null 终止！） */
	int			cre_pat_len;	/* 原始 RE 的长度，以字节为单位 */
	int			cre_flags;		/* 编译标志：扩展，大小写不敏感等 */
	Oid			cre_collation;	/* 要使用的排序 */
	regex_t		cre_re;			/* 编译后的正则表达式 */
} cached_re_str;

static int	num_res = 0;		/* 缓存的正则表达式数量 */
static cached_re_str re_array[MAX_CACHED_RES];	/* 缓存的正则表达式 */


/* 本地函数 */
static regexp_matches_ctx *fc_setup_regexp_matches(text *fc_orig_str, text *fc_pattern,
												pg_re_flags *fc_flags,
												int fc_start_search,
												Oid fc_collation,
												bool fc_use_subpatterns,
												bool fc_ignore_degenerate,
												bool fc_fetching_unmatched);
static ArrayType *fc_build_regexp_match_result(regexp_matches_ctx *fc_matchctx);
static Datum fc_build_regexp_split_result(regexp_matches_ctx *fc_splitctx);


/* 
 * RE_compile_and_cache - 编译 RE，尽可能缓存
 *
 * 返回 regex_t *
 *
 *	text_re --- 模式，以 TEXT 对象表示
 *	cflags --- 模式的编译选项
 *	collation --- 用于 LC_CTYPE 依赖行为的字符串比较
 *
 * 模式以数据库编码给出。我们内部转换为
 * pg_wchar 数组，这正是 Spencer 的正则表达式包所需的。
 */
regex_t *
RE_compile_and_cache(text *fc_text_re, int fc_cflags, Oid fc_collation)
{
	int			fc_text_re_len = VARSIZE_ANY_EXHDR(fc_text_re);
	char	   *fc_text_re_val = VARDATA_ANY(fc_text_re);
	pg_wchar   *fc_pattern;
	int			fc_pattern_len;
	int			fc_i;
	int			fc_regcomp_result;
	cached_re_str fc_re_temp;
	char		fc_errMsg[100];

	/* 
	 * 在先前编译的 RE 中查找匹配。由于数据
	 * 结构是自组织的，使用频率最高的条目在前面，我们的
	 * 搜索策略可以只是从前面扫描。
	 */
	for (fc_i = 0; fc_i < num_res; fc_i++)
	{
		if (re_array[fc_i].cre_pat_len == fc_text_re_len &&
			re_array[fc_i].cre_flags == fc_cflags &&
			re_array[fc_i].cre_collation == fc_collation &&
			memcmp(re_array[fc_i].cre_pat, fc_text_re_val, fc_text_re_len) == 0)
		{
			/*
			 * 找到匹配；如果不在那就移到前面。
			 */
			if (fc_i > 0)
			{
				fc_re_temp = re_array[fc_i];
				memmove(&re_array[1], &re_array[0], fc_i * sizeof(cached_re_str));
				re_array[0] = fc_re_temp;
			}

			return &re_array[0].cre_re;
		}
	}

	/*
	 * 找不到，所以尝试编译新的 RE。为避免在失败时泄露
	 * 资源，我们将结果构建到 re_temp 本地。
	 */

	/* 将模式字符串转换为宽字符 */
	fc_pattern = (pg_wchar *) palloc((fc_text_re_len + 1) * sizeof(pg_wchar));
	fc_pattern_len = pg_mb2wchar_with_len(fc_text_re_val,
									   fc_pattern,
									   fc_text_re_len);

	fc_regcomp_result = pg_regcomp(&fc_re_temp.cre_re,
								fc_pattern,
								fc_pattern_len,
								fc_cflags,
								fc_collation);

	pfree(fc_pattern);

	if (fc_regcomp_result != REG_OKAY)
	{
		/* re 未成功编译（如果是这样，则无需 pg_regfree） */

		/*
		 * 在此文件的其他地方，也要在报告正则表达式错误之前进行 CHECK_FOR_INTERRUPTS。
		 * 这样，如果正则表达式库中止并返回 REG_CANCEL，我们就不会打印出暗示正则表达式无效的错误信息。
		 */
		CHECK_FOR_INTERRUPTS();

		pg_regerror(fc_regcomp_result, &fc_re_temp.cre_re, fc_errMsg, sizeof(fc_errMsg));
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
				 errmsg("invalid regular expression: %s", fc_errMsg)));
	}

	/*
	 * 我们对 cre_pat 字段使用 malloc/free，因为存储必须
	 * 在事务之间保持，并且因为我们希望在内存不足时能够控制返回。
	 * Max() 是因为某些 malloc 实现为 malloc(0) 返回 NULL。
	 */
	fc_re_temp.cre_pat = malloc(Max(fc_text_re_len, 1));
	if (fc_re_temp.cre_pat == NULL)
	{
		pg_regfree(&fc_re_temp.cre_re);
		ereport(ERROR,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));
	}
	memcpy(fc_re_temp.cre_pat, fc_text_re_val, fc_text_re_len);
	fc_re_temp.cre_pat_len = fc_text_re_len;
	fc_re_temp.cre_flags = fc_cflags;
	fc_re_temp.cre_collation = fc_collation;

	/*
	 * 好吧，我们在 re_temp 中有一个有效的新条目；将其插入到存储
	 * 数组中。如果需要，则丢弃最后一个条目。
	 */
	if (num_res >= MAX_CACHED_RES)
	{
		--num_res;
		Assert(num_res < MAX_CACHED_RES);
		pg_regfree(&re_array[num_res].cre_re);
		free(re_array[num_res].cre_pat);
	}

	if (num_res > 0)
		memmove(&re_array[1], &re_array[0], num_res * sizeof(cached_re_str));

	re_array[0] = fc_re_temp;
	num_res++;

	return &re_array[0].cre_re;
}

/* 
 * RE_wchar_execute - 在 pg_wchar 数据上执行 RE
 *
 * 匹配时返回 true，不匹配时返回 false
 *
 *	re --- 由 RE_compile_and_cache 返回的编译模式
 *	data --- 要匹配的数据（无需以 null 终止）
 *	data_len --- 数据字符串的长度
 *	start_search -- 开始搜索的数据偏移量
 *	nmatch, pmatch --- 匹配细节的可选返回区域
 *
 * 数据以 pg_wchar 数组的形式给出，这正是 Spencer 的正则表达式包所需的。
 */
static bool fc_RE_wchar_execute(regex_t *fc_re, pg_wchar *fc_data, int fc_data_len,
				 int fc_start_search, int fc_nmatch, regmatch_t *fc_pmatch)
{
	int			fc_regexec_result;
	char		fc_errMsg[100];

	/* 执行 RE 匹配并返回结果 */
	fc_regexec_result = pg_regexec(fc_re,
								fc_data,
								fc_data_len,
								fc_start_search,
								NULL,	/* 无细节 */
								fc_nmatch,
								fc_pmatch,
								0);

	if (fc_regexec_result != REG_OKAY && fc_regexec_result != REG_NOMATCH)
	{
		/* re 失败??? */
		CHECK_FOR_INTERRUPTS();
		pg_regerror(fc_regexec_result, fc_re, fc_errMsg, sizeof(fc_errMsg));
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
				 errmsg("regular expression failed: %s", fc_errMsg)));
	}

	return (fc_regexec_result == REG_OKAY);
}

/*
 * RE_execute - 执行一个正则表达式
 *
 * 匹配成功返回true，未匹配返回false
 *
 *	re --- 由RE_compile_and_cache返回的编译模式
 *	dat --- 要匹配的数据（不需要以null结尾）
 *	dat_len --- 数据字符串的长度
 *	nmatch, pmatch --- 可选返回匹配详细信息的区域
 *
 * 数据以数据库编码提供。我们内部转换为pg_wchar数组，
 * 这是Spencer的正则表达式包所需要的。
 */
static bool fc_RE_execute(regex_t *fc_re, char *fc_dat, int fc_dat_len,
		   int fc_nmatch, regmatch_t *fc_pmatch)
{
	pg_wchar   *fc_data;
	int			fc_data_len;
	bool		fc_match;

	/* 将数据字符串转换为宽字符 */
	fc_data = (pg_wchar *) palloc((fc_dat_len + 1) * sizeof(pg_wchar));
	fc_data_len = pg_mb2wchar_with_len(fc_dat, fc_data, fc_dat_len);

	/* 执行 RE 匹配并返回结果 */
	fc_match = fc_RE_wchar_execute(fc_re, fc_data, fc_data_len, 0, fc_nmatch, fc_pmatch);

	pfree(fc_data);
	return fc_match;
}

/*
 * RE_compile_and_execute - 编译并执行一个正则表达式
 *
 * 匹配成功返回true，未匹配返回false
 *
 *	text_re --- 以文本对象表示的模式
 *	dat --- 要匹配的数据（不需要以null结尾）
 *	dat_len --- 数据字符串的长度
 *	cflags --- 模式的编译选项
 *	collation --- 用于LC_CTYPE依赖行为的排序规则
 *	nmatch, pmatch --- 可选返回匹配详细信息的区域
 *
 * 模式和数据都以数据库编码提供。我们内部
 * 转换为pg_wchar数组，这是Spencer的正则表达式包所要的。
 */
bool RE_compile_and_execute(text *fc_text_re, char *fc_dat, int fc_dat_len,
					   int fc_cflags, Oid fc_collation,
					   int fc_nmatch, regmatch_t *fc_pmatch)
{
	regex_t    *fc_re;

	/* 如果调用者不想要子匹配详细信息，请使用REG_NOSUB */
	if (fc_nmatch < 2)
		fc_cflags |= REG_NOSUB;

	/* 编译 RE */
	fc_re = RE_compile_and_cache(fc_text_re, fc_cflags, fc_collation);

	return fc_RE_execute(fc_re, fc_dat, fc_dat_len, fc_nmatch, fc_pmatch);
}


/*
 * parse_re_flags - 解析 regexp_match 和相关函数的选项参数
 *
 *	flags --- 输出参数，填写所需选项
 *	opts --- TEXT 对象，或 NULL 表示默认值
 *
 * 这接受所有调用者允许的选项；不希望某些选项的调用者必须在事后拒绝它们。
 */
static void fc_parse_re_flags(pg_re_flags *fc_flags, text *fc_opts)
{
	/* regex 风味总是折叠到编译标志中 */
	fc_flags->cflags = REG_ADVANCED;
	fc_flags->glob = false;

	if (fc_opts)
	{
		char	   *fc_opt_p = VARDATA_ANY(fc_opts);
		int			fc_opt_len = VARSIZE_ANY_EXHDR(fc_opts);
		int			fc_i;

		for (fc_i = 0; fc_i < fc_opt_len; fc_i++)
		{
			switch (fc_opt_p[fc_i])
			{
				case 'g':
					fc_flags->glob = true;
					break;
				case 'b':		/* BREs（但为什么？？？） */
					fc_flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED | REG_QUOTE);
					break;
				case 'c':		/* 区分大小写 */
					fc_flags->cflags &= ~REG_ICASE;
					break;
				case 'e':		/* 普通 EREs */
					fc_flags->cflags |= REG_EXTENDED;
					fc_flags->cflags &= ~(REG_ADVANCED | REG_QUOTE);
					break;
				case 'i':		/* 不区分大小写 */
					fc_flags->cflags |= REG_ICASE;
					break;
				case 'm':		/* Perloid 的同义词 n */
				case 'n':		/* \n 影响 ^ $ . [^ */
					fc_flags->cflags |= REG_NEWLINE;
					break;
				case 'p':		/* ~Perl, \n 影响 . [^ */
					fc_flags->cflags |= REG_NLSTOP;
					fc_flags->cflags &= ~REG_NLANCH;
					break;
				case 'q':		/* 字面字符串 */
					fc_flags->cflags |= REG_QUOTE;
					fc_flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED);
					break;
				case 's':		/* 单行，\n 普通 */
					fc_flags->cflags &= ~REG_NEWLINE;
					break;
				case 't':		/* 紧凑语法 */
					fc_flags->cflags &= ~REG_EXPANDED;
					break;
				case 'w':		/* 奇怪，\n 仅影响 ^ $ */
					fc_flags->cflags &= ~REG_NLSTOP;
					fc_flags->cflags |= REG_NLANCH;
					break;
				case 'x':		/* 扩展语法 */
					fc_flags->cflags |= REG_EXPANDED;
					break;
				default:
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid regular expression option: \"%.*s\"",
									pg_mblen(fc_opt_p + fc_i), fc_opt_p + fc_i)));
					break;
			}
		}
	}
}


/* 
 *	接口例程由函数管理器调用
 */

Datum nameregexeq(PG_FUNCTION_ARGS)
{
	Name		fc_n = PG_GETARG_NAME(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(RE_compile_and_execute(fc_p,
										  NameStr(*fc_n),
										  strlen(NameStr(*fc_n)),
										  REG_ADVANCED,
										  PG_GET_COLLATION(),
										  0, NULL));
}

Datum nameregexne(PG_FUNCTION_ARGS)
{
	Name		fc_n = PG_GETARG_NAME(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(!RE_compile_and_execute(fc_p,
										   NameStr(*fc_n),
										   strlen(NameStr(*fc_n)),
										   REG_ADVANCED,
										   PG_GET_COLLATION(),
										   0, NULL));
}

Datum textregexeq(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(RE_compile_and_execute(fc_p,
										  VARDATA_ANY(fc_s),
										  VARSIZE_ANY_EXHDR(fc_s),
										  REG_ADVANCED,
										  PG_GET_COLLATION(),
										  0, NULL));
}

Datum textregexne(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(!RE_compile_and_execute(fc_p,
										   VARDATA_ANY(fc_s),
										   VARSIZE_ANY_EXHDR(fc_s),
										   REG_ADVANCED,
										   PG_GET_COLLATION(),
										   0, NULL));
}


/*
 * 使用 regexp 相关内容的例程，但忽略大小写。
 * 为此，我们将 REG_ICASE 标志用于 pg_regcomp。
 */


Datum nameicregexeq(PG_FUNCTION_ARGS)
{
	Name		fc_n = PG_GETARG_NAME(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(RE_compile_and_execute(fc_p,
										  NameStr(*fc_n),
										  strlen(NameStr(*fc_n)),
										  REG_ADVANCED | REG_ICASE,
										  PG_GET_COLLATION(),
										  0, NULL));
}

Datum nameicregexne(PG_FUNCTION_ARGS)
{
	Name		fc_n = PG_GETARG_NAME(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(!RE_compile_and_execute(fc_p,
										   NameStr(*fc_n),
										   strlen(NameStr(*fc_n)),
										   REG_ADVANCED | REG_ICASE,
										   PG_GET_COLLATION(),
										   0, NULL));
}

Datum texticregexeq(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(RE_compile_and_execute(fc_p,
										  VARDATA_ANY(fc_s),
										  VARSIZE_ANY_EXHDR(fc_s),
										  REG_ADVANCED | REG_ICASE,
										  PG_GET_COLLATION(),
										  0, NULL));
}

Datum texticregexne(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);

	PG_RETURN_BOOL(!RE_compile_and_execute(fc_p,
										   VARDATA_ANY(fc_s),
										   VARSIZE_ANY_EXHDR(fc_s),
										   REG_ADVANCED | REG_ICASE,
										   PG_GET_COLLATION(),
										   0, NULL));
}


/*
 * textregexsubstr()
 *	返回由正则表达式匹配的子字符串。
 */
Datum textregexsubstr(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);
	regex_t    *fc_re;
	regmatch_t	fc_pmatch[2];
	int			fc_so,
				fc_eo;

	/* 编译 RE */
	fc_re = RE_compile_and_cache(fc_p, REG_ADVANCED, PG_GET_COLLATION());

	/*
	 * 我们传递两个 regmatch_t 结构来获取关于整体匹配和
	 * 第一个括号内子表达式（如果有）的匹配信息。如果有
	 * 括号内的子表达式，我们返回它所匹配的内容；否则
	 * 返回整个正则表达式所匹配的内容。
	 */
	if (!fc_RE_execute(fc_re,
					VARDATA_ANY(fc_s), VARSIZE_ANY_EXHDR(fc_s),
					2, fc_pmatch))
		PG_RETURN_NULL();		/* 确定没有匹配 */

	if (fc_re->re_nsub > 0)
	{
		/* 有括号内子表达式，使用第一个 */
		fc_so = fc_pmatch[1].rm_so;
		fc_eo = fc_pmatch[1].rm_eo;
	}
	else
	{
		/* 没有括号内子表达式，使用整体匹配 */
		fc_so = fc_pmatch[0].rm_so;
		fc_eo = fc_pmatch[0].rm_eo;
	}

	/*
	 * 可能会有对整个模式的匹配，但没有对子表达式的匹配；例如 'foo(bar)?' 被认为匹配 'foo'，
	 * 但没有子表达式匹配。因此这个额外的匹配失败测试
	 * 并不是多余的。
	 */
	if (fc_so < 0 || fc_eo < 0)
		PG_RETURN_NULL();

	return DirectFunctionCall3(text_substr,
							   PointerGetDatum(fc_s),
							   Int32GetDatum(fc_so + 1),
							   Int32GetDatum(fc_eo - fc_so));
}

/*
 * textregexreplace_noopt()
 *	 返回由正则表达式匹配的字符串，并进行替换。
 *
 * 此版本没有选项参数：我们默认为区分大小写的
 * 匹配，仅替换第一个实例。
 */
Datum textregexreplace_noopt(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);
	text	   *fc_r = PG_GETARG_TEXT_PP(2);

	PG_RETURN_TEXT_P(replace_text_regexp(fc_s, fc_p, fc_r,
										 REG_ADVANCED, PG_GET_COLLATION(),
										 0, 1));
}

/*
 * textregexreplace()
 *	 返回由正则表达式匹配的字符串，并进行替换。
 */
Datum textregexreplace(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);
	text	   *fc_r = PG_GETARG_TEXT_PP(2);
	text	   *fc_opt = PG_GETARG_TEXT_PP(3);
	pg_re_flags fc_flags;

	/*
	 * regexp_replace() 带有四个参数时，当第四个参数为 UNKNOWN 类型时将优先解析为
	 * 这种形式。然而，用户可能意图调用 textregexreplace_extended_no_n。如果我们
	 * 看到类似整数的标志，发出与 parse_re_flags 相同的错误，但添加一个提示
	 * 关于如何修复它。
	 */
	if (VARSIZE_ANY_EXHDR(fc_opt) > 0)
	{
		char	   *fc_opt_p = VARDATA_ANY(fc_opt);

		if (*fc_opt_p >= '0' && *fc_opt_p <= '9')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid regular expression option: \"%.*s\"",
							pg_mblen(fc_opt_p), fc_opt_p),
					 errhint("If you meant to use regexp_replace() with a start parameter, cast the fourth argument to integer explicitly.")));
	}

	fc_parse_re_flags(&fc_flags, fc_opt);

	PG_RETURN_TEXT_P(replace_text_regexp(fc_s, fc_p, fc_r,
										 fc_flags.cflags, PG_GET_COLLATION(),
										 0, fc_flags.glob ? 0 : 1));
}

/*
 * textregexreplace_extended()
 *	 返回由正则表达式匹配的字符串，并进行替换。
 *	 通过允许起始位置和选择替换的出现实例（0 表示所有出现）来扩展 textregexreplace。
 */
Datum textregexreplace_extended(PG_FUNCTION_ARGS)
{
	text	   *fc_s = PG_GETARG_TEXT_PP(0);
	text	   *fc_p = PG_GETARG_TEXT_PP(1);
	text	   *fc_r = PG_GETARG_TEXT_PP(2);
	int			fc_start = 1;
	int			fc_n = 1;
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(5);
	pg_re_flags fc_re_flags;

	/* 收集可选参数 */
	if (PG_NARGS() > 3)
	{
		fc_start = PG_GETARG_INT32(3);
		if (fc_start <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"start", fc_start)));
	}
	if (PG_NARGS() > 4)
	{
		fc_n = PG_GETARG_INT32(4);
		if (fc_n < 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"n", fc_n)));
	}

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);

	/* 如果未指定 N，则从 'g' 标志中推断 */
	if (PG_NARGS() <= 4)
		fc_n = fc_re_flags.glob ? 0 : 1;

	/* 执行替换 */
	PG_RETURN_TEXT_P(replace_text_regexp(fc_s, fc_p, fc_r,
										 fc_re_flags.cflags, PG_GET_COLLATION(),
										 fc_start - 1, fc_n));
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum textregexreplace_extended_no_n(PG_FUNCTION_ARGS)
{
	return textregexreplace_extended(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum textregexreplace_extended_no_flags(PG_FUNCTION_ARGS)
{
	return textregexreplace_extended(fcinfo);
}

/*
 * similar_to_escape(), similar_escape()
 *
 * 将 SQL "SIMILAR TO" 的正则表达式模式转化为 POSIX 风格，以便可以由
 * 我们的正则表达式引擎使用。
 *
 * similar_escape_internal() 是三个 SQL 公开的函数的公共工作马。
 * esc_text 可以作为 NULL 传递以选择默认转义（即 '\'），或作为空字符串以选择无转义字符。
 */
static text * fc_similar_escape_internal(text *fc_pat_text, text *fc_esc_text)
{
	text	   *fc_result;
	char	   *fc_p,
			   *fc_e,
			   *fc_r;
	int			fc_plen,
				fc_elen;
	bool		fc_afterescape = false;
	bool		fc_incharclass = false;
	int			fc_nquotes = 0;

	fc_p = VARDATA_ANY(fc_pat_text);
	fc_plen = VARSIZE_ANY_EXHDR(fc_pat_text);
	if (fc_esc_text == NULL)
	{
		/* 未提供 ESCAPE 子句；默认为反斜杠作为转义 */
		fc_e = "\\";
		fc_elen = 1;
	}
	else
	{
		fc_e = VARDATA_ANY(fc_esc_text);
		fc_elen = VARSIZE_ANY_EXHDR(fc_esc_text);
		if (fc_elen == 0)
			fc_e = NULL;			/* 无转义字符 */
		else if (fc_elen > 1)
		{
			int			fc_escape_mblen = pg_mbstrlen_with_len(fc_e, fc_elen);

			if (fc_escape_mblen > 1)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
						 errmsg("invalid escape string"),
						 errhint("Escape string must be empty or one character.")));
		}
	}

	/*----------
	 * 我们将转换后的输入字符串包围在
	 *			^(?: ... )$
	 * 这需要一些解释。我们需要 "^" 和 "$" 以强制
	 * 模式匹配整个输入字符串，符合 SQL 规范。
	 * "(?:" 和 ")" 是不捕获的括号集；我们必须有
	 * 括号，以防字符串包含 "|"，否则 "^" 和 "$" 将
	 * 被绑定到第一个和最后一个选项上，这不是我们
	 * 想要的，且括号必须为非捕获式，因为我们不想在
	 * 为 SUBSTRING 选择输出时计算它们。
	 *
	 * 当模式被转义双引号分为三部分时，
	 * 我们发出的内容是
	 *			^(?:part1){1,1}?(part2){1,1}(?:part3)$
	 * 这要求更多的解释。part1 上的 "{1,1}?" 使其
	 * 为非贪婪模式，以便匹配尽可能少的文本
	 * 而不是最多的，这符合 SQL 的要求。part2 周围的
	 * 普通括号是捕获型括号，这样那部分就控制了
	 * SUBSTRING 的结果。"{1,1}" 强制 part2 为
	 * 贪婪模式，以便匹配尽可能多的文本；因此 part3 必须匹配
	 * 最少的文本，这也是 SQL 的要求。我们不需要在 part3 上
	 * 明确的贪婪标记。请注意，这也限制了任何 "|" 字符的影响
	 * 在各自的部分中，这正是我们想要的。
	 *
	 * SQL 规范规定 SUBSTRING 的模式必须包含恰好两个
	 * 转义双引号，但如果超过两个我们才会抱怨。
	 * 如果没有，我们的行为就像 part1 和 part3 是空；如果有一个，我们
	 * 的行为就像 part3 是空。两种行为都源于在上述扩展中省略
	 * 相关部分分隔符。如果这个函数的结果用于普通的正则表达式匹配
	 * (SIMILAR TO)，转义双引号对匹配行为没有影响。
	 *----------
	 */

	/*
	 * 我们需要为前缀/后缀和部分分隔符留出空间，加上每个输入字节最多
	 * 3 个输出字节；因为输入最多是 1GB，因此这不会超出 size_t。
	 */
	fc_result = (text *) palloc(VARHDRSZ + 23 + 3 * (size_t) fc_plen);
	fc_r = VARDATA(fc_result);

	*fc_r++ = '^';
	*fc_r++ = '(';
	*fc_r++ = '?';
	*fc_r++ = ':';

	while (fc_plen > 0)
	{
		char		fc_pchar = *fc_p;

		
/*
		 * 如果转义字符和模式中的当前字符都是多字节，我们需要采取慢路径。
		 *
		 * 但是，如果其中一个是单字节，我们可以逐字节处理模式，忽略多字节字符。 （这有效，因为所有服务器编码都有一个属性，即有效的多字节字符表示不能包含有效单字节字符的表示。）
		 */

		if (fc_elen > 1)
		{
			int			fc_mblen = pg_mblen(fc_p);

			if (fc_mblen > 1)
			{
				/* 慢的多字节路径 */
				if (fc_afterescape)
				{
					*fc_r++ = '\\';
					memcpy(fc_r, fc_p, fc_mblen);
					fc_r += fc_mblen;
					fc_afterescape = false;
				}
				else if (fc_e && fc_elen == fc_mblen && memcmp(fc_e, fc_p, fc_mblen) == 0)
				{
					/* SQL 转义字符；不发送到输出 */
					fc_afterescape = true;
				}
				else
				{
					/*
					 * 我们知道它是一个多字节字符，所以我们不需要
					 * 做所有与单字节字符的比较
					 * 如下所示。
					 */
					memcpy(fc_r, fc_p, fc_mblen);
					fc_r += fc_mblen;
				}

				fc_p += fc_mblen;
				fc_plen -= fc_mblen;

				continue;
			}
		}

		/* 快速路径 */
		if (fc_afterescape)
		{
			if (fc_pchar == '"' && !fc_incharclass)	/* 转义双引号？ */
			{
				/* 根据上述注释发出适当的部分分隔符 */
				if (fc_nquotes == 0)
				{
					*fc_r++ = ')';
					*fc_r++ = '{';
					*fc_r++ = '1';
					*fc_r++ = ',';
					*fc_r++ = '1';
					*fc_r++ = '}';
					*fc_r++ = '?';
					*fc_r++ = '(';
				}
				else if (fc_nquotes == 1)
				{
					*fc_r++ = ')';
					*fc_r++ = '{';
					*fc_r++ = '1';
					*fc_r++ = ',';
					*fc_r++ = '1';
					*fc_r++ = '}';
					*fc_r++ = '(';
					*fc_r++ = '?';
					*fc_r++ = ':';
				}
				else
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER),
							 errmsg("SQL regular expression may not contain more than two escape-double-quote separators")));
				fc_nquotes++;
			}
			else
			{
				/*
				 * 我们允许任何字符被转义；特别是，这
				 * 允许访问 POSIX 字符类转义，例如
				 * "\d"。SQL 规范限制要严格得多。
				 */
				*fc_r++ = '\\';
				*fc_r++ = fc_pchar;
			}
			fc_afterescape = false;
		}
		else if (fc_e && fc_pchar == *fc_e)
		{
			/* SQL 转义字符；不发送到输出 */
			fc_afterescape = true;
		}
		else if (fc_incharclass)
		{
			if (fc_pchar == '\\')
				*fc_r++ = '\\';
			*fc_r++ = fc_pchar;
			if (fc_pchar == ']')
				fc_incharclass = false;
		}
		else if (fc_pchar == '[')
		{
			*fc_r++ = fc_pchar;
			fc_incharclass = true;
		}
		else if (fc_pchar == '%')
		{
			*fc_r++ = '.';
			*fc_r++ = '*';
		}
		else if (fc_pchar == '_')
			*fc_r++ = '.';
		else if (fc_pchar == '(')
		{
			/* 转换为非捕获括号 */
			*fc_r++ = '(';
			*fc_r++ = '?';
			*fc_r++ = ':';
		}
		else if (fc_pchar == '\\' || fc_pchar == '.' ||
				 fc_pchar == '^' || fc_pchar == '$')
		{
			*fc_r++ = '\\';
			*fc_r++ = fc_pchar;
		}
		else
			*fc_r++ = fc_pchar;
		fc_p++, fc_plen--;
	}

	*fc_r++ = ')';
	*fc_r++ = '$';

	SET_VARSIZE(fc_result, fc_r - ((char *) fc_result));

	return fc_result;
}

/*
 * similar_to_escape(pattern, escape)
 */
Datum similar_to_escape_2(PG_FUNCTION_ARGS)
{
	text	   *fc_pat_text = PG_GETARG_TEXT_PP(0);
	text	   *fc_esc_text = PG_GETARG_TEXT_PP(1);
	text	   *fc_result;

	fc_result = fc_similar_escape_internal(fc_pat_text, fc_esc_text);

	PG_RETURN_TEXT_P(fc_result);
}

/*
 * similar_to_escape(pattern)
 * 插入默认转义字符。
 */
Datum similar_to_escape_1(PG_FUNCTION_ARGS)
{
	text	   *fc_pat_text = PG_GETARG_TEXT_PP(0);
	text	   *fc_result;

	fc_result = fc_similar_escape_internal(fc_pat_text, NULL);

	PG_RETURN_TEXT_P(fc_result);
}

/*
 * similar_escape(pattern, escape)
 *
 * 兼容使用 v13 之前的 SIMILAR TO 扩展存储的视图的遗留函数。
 * 与上述函数不同，这个函数不是严格的，这导致了对 "ESCAPE NULL" 的处理不符合规范。
 */
Datum similar_escape(PG_FUNCTION_ARGS)
{
	text	   *fc_pat_text;
	text	   *fc_esc_text;
	text	   *fc_result;

	/* 这个函数不是严格的，所以必须明确测试 */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();
	fc_pat_text = PG_GETARG_TEXT_PP(0);

	if (PG_ARGISNULL(1))
		fc_esc_text = NULL;		/* 使用默认的转义字符 */
	else
		fc_esc_text = PG_GETARG_TEXT_PP(1);

	fc_result = fc_similar_escape_internal(fc_pat_text, fc_esc_text);

	PG_RETURN_TEXT_P(fc_result);
}

/*
 * regexp_count()
 *		返回字符串中匹配模式的数量。
 */
Datum regexp_count(PG_FUNCTION_ARGS)
{
	text	   *fc_str = PG_GETARG_TEXT_PP(0);
	text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
	int			fc_start = 1;
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(3);
	pg_re_flags fc_re_flags;
	regexp_matches_ctx *fc_matchctx;

	/* 收集可选参数 */
	if (PG_NARGS() > 2)
	{
		fc_start = PG_GETARG_INT32(2);
		if (fc_start <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"start", fc_start)));
	}

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_count()")));
	/* 但我们仍然找到所有的匹配 */
	fc_re_flags.glob = true;

	/* 进行匹配 */
	fc_matchctx = fc_setup_regexp_matches(fc_str, fc_pattern, &fc_re_flags, fc_start - 1,
									PG_GET_COLLATION(),
									false,	/* 可以忽略子表达式 */
									false, false);

	PG_RETURN_INT32(fc_matchctx->nmatches);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_count_no_start(PG_FUNCTION_ARGS)
{
	return regexp_count(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_count_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_count(fcinfo);
}

/*
 * regexp_instr()
 *		返回匹配在字符串中的位置
 */
Datum regexp_instr(PG_FUNCTION_ARGS)
{
	text	   *fc_str = PG_GETARG_TEXT_PP(0);
	text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
	int			fc_start = 1;
	int			fc_n = 1;
	int			fc_endoption = 0;
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(5);
	int			fc_subexpr = 0;
	int			fc_pos;
	pg_re_flags fc_re_flags;
	regexp_matches_ctx *fc_matchctx;

	/* 收集可选参数 */
	if (PG_NARGS() > 2)
	{
		fc_start = PG_GETARG_INT32(2);
		if (fc_start <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"start", fc_start)));
	}
	if (PG_NARGS() > 3)
	{
		fc_n = PG_GETARG_INT32(3);
		if (fc_n <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"n", fc_n)));
	}
	if (PG_NARGS() > 4)
	{
		fc_endoption = PG_GETARG_INT32(4);
		if (fc_endoption != 0 && fc_endoption != 1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"endoption", fc_endoption)));
	}
	if (PG_NARGS() > 6)
	{
		fc_subexpr = PG_GETARG_INT32(6);
		if (fc_subexpr < 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"subexpr", fc_subexpr)));
	}

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_instr()")));
	/* 但我们仍然找到所有的匹配 */
	fc_re_flags.glob = true;

	/* 进行匹配 */
	fc_matchctx = fc_setup_regexp_matches(fc_str, fc_pattern, &fc_re_flags, fc_start - 1,
									PG_GET_COLLATION(),
									(fc_subexpr > 0),	/* 需要子匹配吗？ */
									false, false);

	/* 当 n 超过匹配时返回 0（包括没有匹配的情况） */
	if (fc_n > fc_matchctx->nmatches)
		PG_RETURN_INT32(0);

	/* 当子表达式超过子表达式的数量时返回 0 */
	if (fc_subexpr > fc_matchctx->npatterns)
		PG_RETURN_INT32(0);

	/* 选择适当的匹配位置返回 */
	fc_pos = (fc_n - 1) * fc_matchctx->npatterns;
	if (fc_subexpr > 0)
		fc_pos += fc_subexpr - 1;
	fc_pos *= 2;
	if (fc_endoption == 1)
		fc_pos += 1;

	if (fc_matchctx->match_locs[fc_pos] >= 0)
		PG_RETURN_INT32(fc_matchctx->match_locs[fc_pos] + 1);
	else
		PG_RETURN_INT32(0);		/* 位置无法识别 */
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_instr_no_start(PG_FUNCTION_ARGS)
{
	return regexp_instr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_instr_no_n(PG_FUNCTION_ARGS)
{
	return regexp_instr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_instr_no_endoption(PG_FUNCTION_ARGS)
{
	return regexp_instr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_instr_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_instr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_instr_no_subexpr(PG_FUNCTION_ARGS)
{
	return regexp_instr(fcinfo);
}

/*
 * regexp_like()
 *		测试字符串中是否有模式匹配。
 */
Datum regexp_like(PG_FUNCTION_ARGS)
{
	text	   *fc_str = PG_GETARG_TEXT_PP(0);
	text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
	pg_re_flags fc_re_flags;

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_like()")));

	/* 否则它就像 textregexeq/texticregexeq */
	PG_RETURN_BOOL(RE_compile_and_execute(fc_pattern,
										  VARDATA_ANY(fc_str),
										  VARSIZE_ANY_EXHDR(fc_str),
										  fc_re_flags.cflags,
										  PG_GET_COLLATION(),
										  0, NULL));
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_like_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_like(fcinfo);
}

/*
 * regexp_match()
 *		返回字符串中第一个匹配模式的子字符串。
 */
Datum regexp_match(PG_FUNCTION_ARGS)
{
	text	   *fc_orig_str = PG_GETARG_TEXT_PP(0);
	text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
	pg_re_flags fc_re_flags;
	regexp_matches_ctx *fc_matchctx;

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_match()"),
				 errhint("Use the regexp_matches function instead.")));

	fc_matchctx = fc_setup_regexp_matches(fc_orig_str, fc_pattern, &fc_re_flags, 0,
									PG_GET_COLLATION(), true, false, false);

	if (fc_matchctx->nmatches == 0)
		PG_RETURN_NULL();

	Assert(fc_matchctx->nmatches == 1);

	/* 创建 build_regexp_match_result 需要的工作空间 */
	fc_matchctx->elems = (Datum *) palloc(sizeof(Datum) * fc_matchctx->npatterns);
	fc_matchctx->nulls = (bool *) palloc(sizeof(bool) * fc_matchctx->npatterns);

	PG_RETURN_DATUM(PointerGetDatum(fc_build_regexp_match_result(fc_matchctx)));
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_match_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_match(fcinfo);
}

/*
 * regexp_matches()
 *		返回字符串中所有匹配模式的表。
 */
Datum regexp_matches(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	regexp_matches_ctx *fc_matchctx;

	if (SRF_IS_FIRSTCALL())
	{
		text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
		text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
		pg_re_flags fc_re_flags;
		MemoryContext fc_oldcontext;

		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 确定选项 */
		fc_parse_re_flags(&fc_re_flags, fc_flags);

		/* 确保将输入字符串复制到多重调用上下文中 */
		fc_matchctx = fc_setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), fc_pattern,
										&fc_re_flags, 0,
										PG_GET_COLLATION(),
										true, false, false);

		/* 预先创建 build_regexp_match_result 需要的工作空间 */
		fc_matchctx->elems = (Datum *) palloc(sizeof(Datum) * fc_matchctx->npatterns);
		fc_matchctx->nulls = (bool *) palloc(sizeof(bool) * fc_matchctx->npatterns);

		MemoryContextSwitchTo(fc_oldcontext);
		fc_funcctx->user_fctx = (void *) fc_matchctx;
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_matchctx = (regexp_matches_ctx *) fc_funcctx->user_fctx;

	if (fc_matchctx->next_match < fc_matchctx->nmatches)
	{
		ArrayType  *fc_result_ary;

		fc_result_ary = fc_build_regexp_match_result(fc_matchctx);
		fc_matchctx->next_match++;
		SRF_RETURN_NEXT(fc_funcctx, PointerGetDatum(fc_result_ary));
	}

	SRF_RETURN_DONE(fc_funcctx);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_matches_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_matches(fcinfo);
}

/*
 * setup_regexp_matches --- 为 regexp_match、regexp_split 和相关函数
 *		做初始匹配
 *
 * 为了避免在每次调用时重新查找已编译的模式，我们一次性完成所有匹配。
 * 返回的 regexp_matches_ctx 包含所有与模式匹配的子字符串的位置。
 *
 * start_search：在 orig_str 中开始搜索的字符（不是字节）偏移量。
 * 返回的位置相对于 orig_str。
 * use_subpatterns：收集关于匹配到的带括号的子表达式的数据。
 * ignore_degenerate：忽略零长度匹配。
 * fetching_unmatched：调用者希望获取未匹配的子字符串。
 *
 * 我们目前并不假定 fetching_unmatched 是独占的， fetching
 * 匹配文本也会被提取；如果设置了，它的转换缓冲区足够大以获取任何单一的
 * 匹配或未匹配字符串，但不能获得更大的子字符串。
 * （实际上，当拆分时，匹配通常很小，似乎也不值得进一步复杂化代码。）
 */
static regexp_matches_ctx *
fc_setup_regexp_matches(text *fc_orig_str, text *fc_pattern, pg_re_flags *fc_re_flags,
					 int fc_start_search,
					 Oid fc_collation,
					 bool fc_use_subpatterns,
					 bool fc_ignore_degenerate,
					 bool fc_fetching_unmatched)
{
	regexp_matches_ctx *fc_matchctx = palloc0(sizeof(regexp_matches_ctx));
	int			fc_eml = pg_database_encoding_max_length();
	int			fc_orig_len;
	pg_wchar   *fc_wide_str;
	int			fc_wide_len;
	int			fc_cflags;
	regex_t    *fc_cpattern;
	regmatch_t *fc_pmatch;
	int			fc_pmatch_len;
	int			fc_array_len;
	int			fc_array_idx;
	int			fc_prev_match_end;
	int			fc_prev_valid_match_end;
	int			fc_maxlen = 0;		/* 以字符为单位的最大提取长度 */

	/* 保存原始字符串 --- 我们将从中提取结果子字符串 */
	fc_matchctx->orig_str = fc_orig_str;

	/* 将字符串转换为 pg_wchar 格式以进行匹配 */
	fc_orig_len = VARSIZE_ANY_EXHDR(fc_orig_str);
	fc_wide_str = (pg_wchar *) palloc(sizeof(pg_wchar) * (fc_orig_len + 1));
	fc_wide_len = pg_mb2wchar_with_len(VARDATA_ANY(fc_orig_str), fc_wide_str, fc_orig_len);

	/* 设置已编译的模式 */
	fc_cflags = fc_re_flags->cflags;
	if (!fc_use_subpatterns)
		fc_cflags |= REG_NOSUB;
	fc_cpattern = RE_compile_and_cache(fc_pattern, fc_cflags, fc_collation);

	/* 我们想记住子模式吗？ */
	if (fc_use_subpatterns && fc_cpattern->re_nsub > 0)
	{
		fc_matchctx->npatterns = fc_cpattern->re_nsub;
		fc_pmatch_len = fc_cpattern->re_nsub + 1;
	}
	else
	{
		fc_use_subpatterns = false;
		fc_matchctx->npatterns = 1;
		fc_pmatch_len = 1;
	}

	/* RE 包的临时输出空间 */
	fc_pmatch = palloc(sizeof(regmatch_t) * fc_pmatch_len);

	/*
	 * 实际输出空间（如有需要动态增长）
	 *
	 * 使用值 2^n-1，而不是 2^n，因此我们在 2^28-1 达到限制，而不是在 2^27
	 */
	fc_array_len = fc_re_flags->glob ? 255 : 31;
	fc_matchctx->match_locs = (int *) palloc(sizeof(int) * fc_array_len);
	fc_array_idx = 0;

	/* 搜索模式，可能重复进行 */
	fc_prev_match_end = 0;
	fc_prev_valid_match_end = 0;
	while (fc_RE_wchar_execute(fc_cpattern, fc_wide_str, fc_wide_len, fc_start_search,
							fc_pmatch_len, fc_pmatch))
	{
		/*
		 * 如果请求，忽略退化匹配，即在字符串的开始或结束处发生的零长度匹配，或者就在之前匹配之后。
		 */
		if (!fc_ignore_degenerate ||
			(fc_pmatch[0].rm_so < fc_wide_len &&
			 fc_pmatch[0].rm_eo > fc_prev_match_end))
		{
			/* 如果需要，扩大输出空间 */
			while (fc_array_idx + fc_matchctx->npatterns * 2 + 1 > fc_array_len)
			{
				fc_array_len += fc_array_len + 1; /* 2^n-1 => 2^(n+1)-1 */
				if (fc_array_len > MaxAllocSize / sizeof(int))
					ereport(ERROR,
							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
							 errmsg("too many regular expression matches")));
				fc_matchctx->match_locs = (int *) repalloc(fc_matchctx->match_locs,
														sizeof(int) * fc_array_len);
			}

			/* 保存该匹配的位置 */
			if (fc_use_subpatterns)
			{
				int			fc_i;

				for (fc_i = 1; fc_i <= fc_matchctx->npatterns; fc_i++)
				{
					int			fc_so = fc_pmatch[fc_i].rm_so;
					int			fc_eo = fc_pmatch[fc_i].rm_eo;

					fc_matchctx->match_locs[fc_array_idx++] = fc_so;
					fc_matchctx->match_locs[fc_array_idx++] = fc_eo;
					if (fc_so >= 0 && fc_eo >= 0 && (fc_eo - fc_so) > fc_maxlen)
						fc_maxlen = (fc_eo - fc_so);
				}
			}
			else
			{
				int			fc_so = fc_pmatch[0].rm_so;
				int			fc_eo = fc_pmatch[0].rm_eo;

				fc_matchctx->match_locs[fc_array_idx++] = fc_so;
				fc_matchctx->match_locs[fc_array_idx++] = fc_eo;
				if (fc_so >= 0 && fc_eo >= 0 && (fc_eo - fc_so) > fc_maxlen)
					fc_maxlen = (fc_eo - fc_so);
			}
			fc_matchctx->nmatches++;

			/*
			 * 检查上一个有效（非退化或者退化但不被忽略）匹配的结束和当前匹配的开始之间未匹配部分的长度。
			 */
			if (fc_fetching_unmatched &&
				fc_pmatch[0].rm_so >= 0 &&
				(fc_pmatch[0].rm_so - fc_prev_valid_match_end) > fc_maxlen)
				fc_maxlen = (fc_pmatch[0].rm_so - fc_prev_valid_match_end);
			fc_prev_valid_match_end = fc_pmatch[0].rm_eo;
		}
		fc_prev_match_end = fc_pmatch[0].rm_eo;

		/* 如果不是全局匹配，匹配一次后停止 */
		if (!fc_re_flags->glob)
			break;

		/*
		 * 前进搜索位置。通常我们在上一个匹配的结束处开始下一个搜索；但如果匹配是零长度的，我们必须前进一个字符，否则我们就会再次找到相同的匹配。
		 */
		fc_start_search = fc_prev_match_end;
		if (fc_pmatch[0].rm_so == fc_pmatch[0].rm_eo)
			fc_start_search++;
		if (fc_start_search > fc_wide_len)
			break;
	}

	/*
	 * 检查上一个匹配的结束和输入字符串的结束之间未匹配部分的长度。
	 */
	if (fc_fetching_unmatched &&
		(fc_wide_len - fc_prev_valid_match_end) > fc_maxlen)
		fc_maxlen = (fc_wide_len - fc_prev_valid_match_end);

	/*
	 * 为了拆分代码方便，记下字符串的结束位置。
	 */
	fc_matchctx->match_locs[fc_array_idx] = fc_wide_len;

	if (fc_eml > 1)
	{
		int64		fc_maxsiz = fc_eml * (int64) fc_maxlen;
		int			fc_conv_bufsiz;

		/*
		 * 使转换缓冲区足够大以容纳任何感兴趣的子字符串。
		 *
		 * 最坏的情况：假设我们需要最大大小（maxlen*eml），但利用原始字符串的字节长度是任何提取的子字符串字节长度的上限（而且我们知道len+1是安全分配的，因为varlena头比1字节长）。
		 */
		if (fc_maxsiz > fc_orig_len)
			fc_conv_bufsiz = fc_orig_len + 1;
		else
			fc_conv_bufsiz = fc_maxsiz + 1;	/* 安全，因为maxsiz < 2^30 */

		fc_matchctx->conv_buf = palloc(fc_conv_bufsiz);
		fc_matchctx->conv_bufsiz = fc_conv_bufsiz;
		fc_matchctx->wide_str = fc_wide_str;
	}
	else
	{
		/* 如果我们处于单字节字符集，就无需保留宽字符串。 */
		pfree(fc_wide_str);
		fc_matchctx->wide_str = NULL;
		fc_matchctx->conv_buf = NULL;
		fc_matchctx->conv_bufsiz = 0;
	}

	/* 清理临时存储 */
	pfree(fc_pmatch);

	return fc_matchctx;
}

/*
 * build_regexp_match_result - 为当前匹配构建输出数组
 */
static ArrayType * fc_build_regexp_match_result(regexp_matches_ctx *fc_matchctx)
{
	char	   *fc_buf = fc_matchctx->conv_buf;
	Datum	   *fc_elems = fc_matchctx->elems;
	bool	   *fc_nulls = fc_matchctx->nulls;
	int			fc_dims[1];
	int			fc_lbs[1];
	int			fc_loc;
	int			fc_i;

	/* 从原始字符串提取匹配的子字符串 */
	fc_loc = fc_matchctx->next_match * fc_matchctx->npatterns * 2;
	for (fc_i = 0; fc_i < fc_matchctx->npatterns; fc_i++)
	{
		int			fc_so = fc_matchctx->match_locs[fc_loc++];
		int			fc_eo = fc_matchctx->match_locs[fc_loc++];

		if (fc_so < 0 || fc_eo < 0)
		{
			fc_elems[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
		}
		else if (fc_buf)
		{
			int			fc_len = pg_wchar2mb_with_len(fc_matchctx->wide_str + fc_so,
												   fc_buf,
												   fc_eo - fc_so);

			Assert(fc_len < fc_matchctx->conv_bufsiz);
			fc_elems[fc_i] = PointerGetDatum(cstring_to_text_with_len(fc_buf, fc_len));
			fc_nulls[fc_i] = false;
		}
		else
		{
			fc_elems[fc_i] = DirectFunctionCall3(text_substr,
										   PointerGetDatum(fc_matchctx->orig_str),
										   Int32GetDatum(fc_so + 1),
										   Int32GetDatum(fc_eo - fc_so));
			fc_nulls[fc_i] = false;
		}
	}

	/* 并形成一个数组 */
	fc_dims[0] = fc_matchctx->npatterns;
	fc_lbs[0] = 1;
	/* XXX: 这硬编码了关于文本类型的假设 */
	return construct_md_array(fc_elems, fc_nulls, 1, fc_dims, fc_lbs,
							  TEXTOID, -1, false, TYPALIGN_INT);
}

/*
 * regexp_split_to_table()
 *		在模式匹配处拆分字符串，将拆分出的子字符串作为表返回。
 */
Datum regexp_split_to_table(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	regexp_matches_ctx *fc_splitctx;

	if (SRF_IS_FIRSTCALL())
	{
		text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
		text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
		pg_re_flags fc_re_flags;
		MemoryContext fc_oldcontext;

		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 确定选项 */
		fc_parse_re_flags(&fc_re_flags, fc_flags);
		/* 用户不能指定 'g' */
		if (fc_re_flags.glob)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			/* 翻译者：%s 是 SQL 函数名称 */
					 errmsg("%s does not support the \"global\" option",
							"regexp_split_to_table()")));
		/* 但我们仍然找到所有的匹配 */
		fc_re_flags.glob = true;

		/* 确保将输入字符串复制到多重调用上下文中 */
		fc_splitctx = fc_setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), fc_pattern,
										&fc_re_flags, 0,
										PG_GET_COLLATION(),
										false, true, true);

		MemoryContextSwitchTo(fc_oldcontext);
		fc_funcctx->user_fctx = (void *) fc_splitctx;
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_splitctx = (regexp_matches_ctx *) fc_funcctx->user_fctx;

	if (fc_splitctx->next_match <= fc_splitctx->nmatches)
	{
		Datum		fc_result = fc_build_regexp_split_result(fc_splitctx);

		fc_splitctx->next_match++;
		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}

	SRF_RETURN_DONE(fc_funcctx);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_split_to_table_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_split_to_table(fcinfo);
}

/*
 * regexp_split_to_array()
 *		在模式匹配处拆分字符串，将拆分出的子字符串作为数组返回。
 */
Datum regexp_split_to_array(PG_FUNCTION_ARGS)
{
	ArrayBuildState *fc_astate = NULL;
	pg_re_flags fc_re_flags;
	regexp_matches_ctx *fc_splitctx;

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, PG_GETARG_TEXT_PP_IF_EXISTS(2));
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_split_to_array()")));
	/* 但我们仍然找到所有的匹配 */
	fc_re_flags.glob = true;

	fc_splitctx = fc_setup_regexp_matches(PG_GETARG_TEXT_PP(0),
									PG_GETARG_TEXT_PP(1),
									&fc_re_flags, 0,
									PG_GET_COLLATION(),
									false, true, true);

	while (fc_splitctx->next_match <= fc_splitctx->nmatches)
	{
		fc_astate = accumArrayResult(fc_astate,
								  fc_build_regexp_split_result(fc_splitctx),
								  false,
								  TEXTOID,
								  CurrentMemoryContext);
		fc_splitctx->next_match++;
	}

	PG_RETURN_ARRAYTYPE_P(makeArrayResult(fc_astate, CurrentMemoryContext));
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_split_to_array_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_split_to_array(fcinfo);
}

/*
 * build_regexp_split_result - 为当前匹配构建输出字符串
 *
 * 我们返回当前匹配和上一个匹配之间的字符串，或当next_match == nmatches时返回最后一个匹配后的字符串。
 */
static Datum fc_build_regexp_split_result(regexp_matches_ctx *fc_splitctx)
{
	char	   *fc_buf = fc_splitctx->conv_buf;
	int			fc_startpos;
	int			fc_endpos;

	if (fc_splitctx->next_match > 0)
		fc_startpos = fc_splitctx->match_locs[fc_splitctx->next_match * 2 - 1];
	else
		fc_startpos = 0;
	if (fc_startpos < 0)
		elog(ERROR, "invalid match ending position");

	fc_endpos = fc_splitctx->match_locs[fc_splitctx->next_match * 2];
	if (fc_endpos < fc_startpos)
		elog(ERROR, "invalid match starting position");

	if (fc_buf)
	{
		int			fc_len;

		fc_len = pg_wchar2mb_with_len(fc_splitctx->wide_str + fc_startpos,
								   fc_buf,
								   fc_endpos - fc_startpos);
		Assert(fc_len < fc_splitctx->conv_bufsiz);
		return PointerGetDatum(cstring_to_text_with_len(fc_buf, fc_len));
	}
	else
	{
		return DirectFunctionCall3(text_substr,
								   PointerGetDatum(fc_splitctx->orig_str),
								   Int32GetDatum(fc_startpos + 1),
								   Int32GetDatum(fc_endpos - fc_startpos));
	}
}

/*
 * regexp_substr()
 *		返回与正则表达式模式匹配的子字符串
 */
Datum regexp_substr(PG_FUNCTION_ARGS)
{
	text	   *fc_str = PG_GETARG_TEXT_PP(0);
	text	   *fc_pattern = PG_GETARG_TEXT_PP(1);
	int			fc_start = 1;
	int			fc_n = 1;
	text	   *fc_flags = PG_GETARG_TEXT_PP_IF_EXISTS(4);
	int			fc_subexpr = 0;
	int			fc_so,
				fc_eo,
				fc_pos;
	pg_re_flags fc_re_flags;
	regexp_matches_ctx *fc_matchctx;

	/* 收集可选参数 */
	if (PG_NARGS() > 2)
	{
		fc_start = PG_GETARG_INT32(2);
		if (fc_start <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"start", fc_start)));
	}
	if (PG_NARGS() > 3)
	{
		fc_n = PG_GETARG_INT32(3);
		if (fc_n <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"n", fc_n)));
	}
	if (PG_NARGS() > 5)
	{
		fc_subexpr = PG_GETARG_INT32(5);
		if (fc_subexpr < 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid value for parameter \"%s\": %d",
							"subexpr", fc_subexpr)));
	}

	/* 确定选项 */
	fc_parse_re_flags(&fc_re_flags, fc_flags);
	/* 用户不能指定 'g' */
	if (fc_re_flags.glob)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errmsg("%s does not support the \"global\" option",
						"regexp_substr()")));
	/* 但我们仍然找到所有的匹配 */
	fc_re_flags.glob = true;

	/* 进行匹配 */
	fc_matchctx = fc_setup_regexp_matches(fc_str, fc_pattern, &fc_re_flags, fc_start - 1,
									PG_GET_COLLATION(),
									(fc_subexpr > 0),	/* 需要子匹配吗？ */
									false, false);

	/* 当n超过匹配时返回NULL（包括无匹配的情况） */
	if (fc_n > fc_matchctx->nmatches)
		PG_RETURN_NULL();

	/* 当subexpr超过子表达式次数时返回NULL */
	if (fc_subexpr > fc_matchctx->npatterns)
		PG_RETURN_NULL();

	/* 选择适当的匹配位置返回 */
	fc_pos = (fc_n - 1) * fc_matchctx->npatterns;
	if (fc_subexpr > 0)
		fc_pos += fc_subexpr - 1;
	fc_pos *= 2;
	fc_so = fc_matchctx->match_locs[fc_pos];
	fc_eo = fc_matchctx->match_locs[fc_pos + 1];

	if (fc_so < 0 || fc_eo < 0)
		PG_RETURN_NULL();		/* 无法识别的位置 */

	PG_RETURN_DATUM(DirectFunctionCall3(text_substr,
										PointerGetDatum(fc_matchctx->orig_str),
										Int32GetDatum(fc_so + 1),
										Int32GetDatum(fc_eo - fc_so)));
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_substr_no_start(PG_FUNCTION_ARGS)
{
	return regexp_substr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_substr_no_n(PG_FUNCTION_ARGS)
{
	return regexp_substr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_substr_no_flags(PG_FUNCTION_ARGS)
{
	return regexp_substr(fcinfo);
}

/* 这个是单独的，以防止 opr_sanity 回归测试抱怨 */
Datum regexp_substr_no_subexpr(PG_FUNCTION_ARGS)
{
	return regexp_substr(fcinfo);
}

/*
 * regexp_fixed_prefix - 提取正则表达式的固定前缀（如果有）
 *
 * 如果没有固定前缀，则结果为NULL，否则返回一个palloc的字符串。
 * 如果它是精确匹配，而不仅仅是前缀，则*exact返回true。
 */
char * regexp_fixed_prefix(text *fc_text_re, bool fc_case_insensitive, Oid fc_collation,
					bool *fc_exact)
{
	char	   *fc_result;
	regex_t    *fc_re;
	int			fc_cflags;
	int			fc_re_result;
	pg_wchar   *fc_str;
	size_t		fc_slen;
	size_t		fc_maxlen;
	char		fc_errMsg[100];

	*fc_exact = false;				/* 默认结果 */

	/* 编译 RE */
	fc_cflags = REG_ADVANCED;
	if (fc_case_insensitive)
		fc_cflags |= REG_ICASE;

	fc_re = RE_compile_and_cache(fc_text_re, fc_cflags | REG_NOSUB, fc_collation);

	/* 检查是否有固定前缀 */
	fc_re_result = pg_regprefix(fc_re, &fc_str, &fc_slen);

	switch (fc_re_result)
	{
		case REG_NOMATCH:
			return NULL;

		case REG_PREFIX:
			/* 继续进行宽字符转换 */
			break;

		case REG_EXACT:
			*fc_exact = true;
			/* 继续进行宽字符转换 */
			break;

		default:
			/* re 失败??? */
			CHECK_FOR_INTERRUPTS();
			pg_regerror(fc_re_result, fc_re, fc_errMsg, sizeof(fc_errMsg));
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
					 errmsg("regular expression failed: %s", fc_errMsg)));
			break;
	}

	/* 将pg_wchar结果转换回数据库编码 */
	fc_maxlen = pg_database_encoding_max_length() * fc_slen + 1;
	fc_result = (char *) palloc(fc_maxlen);
	fc_slen = pg_wchar2mb_with_len(fc_str, fc_result, fc_slen);
	Assert(fc_slen < fc_maxlen);

	free(fc_str);

	return fc_result;
}
